var COSA = (function () {
    //region Private Members
    'use strict';

    var _encodedValuePair = function (value, propName) {
        var val = value.hasOwnProperty(propName) ? value[propName] : value,
            encodedValue = _encodeValue(val);
        return {
            rawValue: val,
            encodedValue: encodedValue
        };
    },
    _encodeValue = function (value) {
        if (typeof value === typeof '' && value.slice(0, 1) === '{' && value.slice(-1) === '}') {
            value = value.slice(1, -1);
        }
        return (typeof value === 'object' && value.getTime) ? _encodeDate(value) : _crmXmlEncode(value);
    },
    _encodeDate = function (dateTime) {
        var sb = new COSA.Util.StringBuilder();
        sb.append(dateTime.getFullYear());
        sb.concatAndAppend('-', _padNumber(dateTime.getMonth() + 1), '-');
        sb.concatAndAppend(_padNumber(dateTime.getDate()), 'T');
        sb.concatAndAppend(_padNumber(dateTime.getHours()), ':');
        sb.concatAndAppend(_padNumber(dateTime.getMinutes()), ':');
        sb.append(_padNumber(dateTime.getSeconds()));
        return sb.toString();
    };

    var _removePrefix = function ( str ) {
        var arr = str.split(':');
        return arr.length > 1 ? arr[1] : null;
    };

    var _padNumber = function (s, len) {
        len = len || 2;

        s = '' + s;
        while (s.length < len) {
            s = '0' + s;
        }
        return s;
    };

    var _crmXmlEncode = function (s) {
        if ('undefined' === typeof s || 'unknown' === typeof s || null === s) return s;
        else if (typeof s != 'string') s = s.toString();
        return _innerSurrogateAmpersandWorkaround(s);
    };

    var _innerSurrogateAmpersandWorkaround = function (s) {
        var buffer = '';
        var c0;
        for (var cnt = 0; cnt < s.length; cnt++) {
            c0 = s.charCodeAt(cnt);
            if (c0 >= 55296 && c0 <= 57343)
                if (cnt + 1 < s.length) {
                    var c1 = s.charCodeAt(cnt + 1);
                    if (c1 >= 56320 && c1 <= 57343) {
                        buffer += "CRMEntityReferenceOpen" + ((c0 - 55296) * 1024 + (c1 & 1023) + 65536).toString(16) + "CRMEntityReferenceClose";
                        cnt++;
                    }
                    else
                        buffer += String.fromCharCode(c0);
                }
                else buffer += String.fromCharCode(c0);
            else buffer += String.fromCharCode(c0);
        }
        s = buffer;
        buffer = "";
        for (cnt = 0; cnt < s.length; cnt++) {
            c0 = s.charCodeAt(cnt);
            if (c0 >= 55296 && c0 <= 57343)
                buffer += String.fromCharCode(65533);
            else buffer += String.fromCharCode(c0);
        }
        s = buffer;
        s = _htmlEncode(s);
        s = s.replace(/CRMEntityReferenceOpen/g, "&#x");
        s = s.replace(/CRMEntityReferenceClose/g, ";");
        return s;
    };

    var _htmlEncode = function (s) {
        if (s === null || s === "" || s === undefined) return s;
        for (var count = 0, buffer = "", hEncode = "", cnt = 0; cnt < s.length; cnt++) {
            var c = s.charCodeAt(cnt);
            if (c > 96 && c < 123 || c > 64 && c < 91 || c === 32 || c > 47 && c < 58 || c === 46 || c === 44 || c === 45 || c === 95)
                buffer += String.fromCharCode(c);
            else buffer += "&#" + c + ";";
            if (++count === 500) {
                hEncode += buffer;
                buffer = "";
                count = 0;
            }
        }
        if (buffer.length) hEncode += buffer;
        return hEncode;
    };

    var _provideValueAttr = function ( prefix, type,  ns ) {
        var valueAttrs = { 'i:type': prefix.concat(':', type) };
        var namespace = 'xmlns:'.concat(prefix);
        valueAttrs[namespace] = ns;
        return valueAttrs;
    };

    var _toCamelCase = function ( str ) {
        return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {
            return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
        }).replace(/\s+/g, '');
    };

    var _extractType = function ( str ) {
        var res;
        if ( str !== null ) {
            var tmpStr = str.split(':');
            if ( tmpStr.length > 0 ) {
                res = tmpStr[1];
            }
        }
        return res;
    };

    //endregion Private Members

    //region XrmValue

    function XrmValue( value, type ){
        this.value = typeof value != 'undefined' ? value : null;
        this.type = typeof type != 'undefined' ? type : null;
    }

    XrmValue.prototype = {
        serialize: function () {
            return this.encode();
        },
        encode: function () {
            return this.value !== null ? _encodeValue(this.value) : null;
        },
        toKeyValuePair: function ( key, sc ) {
            var valueAttrs,
                value = this.encode(),
                prefix = sc.letter();
            if ( value === null ) {
                valueAttrs = { 'i:nil':'true' }
            } else {
                if ( this.type === null ) {
                    this.type = typeof this.value;
                }
                switch ( this.type ) {
                    case COSA.Const.AttributeType.GUID:
                        valueAttrs = _provideValueAttr(prefix, COSA.Const.AttributeType.GUID, 'http://schemas.microsoft.com/2003/10/Serialization/');
                        break;
                    case COSA.Const.AttributeType.Number:
                        valueAttrs = _provideValueAttr(prefix, this.value % 1 === 0 ? 'int' : 'decimal', 'http://www.w3.org/2001/XMLSchema');
                        break;
                    case COSA.Const.AttributeType.EntityFilters:
                        valueAttrs = _provideValueAttr(prefix, COSA.Const.AttributeType.EntityFilters, 'http://schemas.microsoft.com/xrm/2011/Metadata');
                        break;
                    default:
                        valueAttrs = _provideValueAttr(prefix, this.value instanceof Date ? 'dateTime' : this.type, 'http://www.w3.org/2001/XMLSchema');
                        break;
                }
            }

            return new KeyValuePair(key, value, valueAttrs);
        }
    };

    //endregion XrmValue

    //region Money

    function Money( value ) {
        this.value = typeof value != 'undefined' ? value : null;
        this.type = COSA.Const.AttributeType.Currency;
    }

    Money.prototype = {
        serialize: function () {
            return [{
                tag: 'a:Value',
                val: this.encode()
            }];
        },
        encode: function () {
            return this.value !== null ? _encodeValue(this.value) : null;
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:Money' });
        },
        deserialize: function ( node ) {
            this.value = parseFloat(node.node('a:Value').text());
        }
    };

    //endregion Money

    //region OptionSetValue

    function OptionSetValue ( value, formattedValue ) {
        this.value = typeof value != 'undefined' ? value : null;
        this.formattedValue = typeof formattedValue != 'undefined' ? formattedValue : '';
        this.type = COSA.Const.AttributeType.OptionSet;
    }

    OptionSetValue.prototype = {
        serialize: function ( ) {
            return [{
                tag:'a:Value',
                val: this.encode()
            }];
        },
        encode: function ( ) {
            return this.value !== null ? _encodeValue(this.value) : null;
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:OptionSetValue' });
        },
        deserialize: function ( node ) {
            this.value = parseInt(node.node('a:Value').text());
        }
    };

    //endregion OptionSetValue

    //region EntityReference

    function EntityReference ( logicalName, id ) {
        this.id = typeof id != 'undefined' ? id :'{00000000-0000-0000-0000-000000000000}';
        this.logicalName = typeof logicalName != 'undefined' ? logicalName : '';
        this.name = '';
        this.type = COSA.Const.AttributeType.EntityReference;
    }

    EntityReference.prototype = {
        serialize: function () {
            var encodedValues = this.encode();
            return [{
                tag: 'a:Id',
                val: encodedValues.id
            }, {
                tag: 'a:LogicalName',
                val: encodedValues.logicalName
            }, {
                tag: 'a:Name',
                attr: {'i:nil': 'true'}
            }];
        },
        encode: function () {
            var encodedId = _encodedValuePair(this, 'id'),
                encodedLogicalName = _encodedValuePair(this, 'logicalName');
            return {
                id: encodedId.encodedValue,
                logicalName: encodedLogicalName.encodedValue
            };
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:EntityReference' });
        },
        deserialize: function ( node ) {
            this.id = node.node('a:Id').text();
            this.logicalName = node.node('a:LogicalName').text();
            this.name = node.node('a:Name').text();
        }
    };

    //endregion EntityReference

    //region EntityCollection

    function EntityCollection ( entities ) {
        this.entities = typeof entities != 'undefined' && Array.isArray(entities) ? entities : null;
        this.type = COSA.Const.AttributeType.EntityCollection;
    }

    EntityCollection.prototype = {
        serialize: function ( serializationContext ) {
            var entityNodes = this.entities.map( function ( entity ) {
                return {
                    tag: 'a:Entity',
                    nodes: entity.serialize( serializationContext )
                };
            });

            return {
                tag: 'a:Entities',
                nodes: entityNodes
            };
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:EntityCollection' })
        },
        deserialize: function ( node, context ) {
            var entitiesNode = node.node('a:Entities'),
                entityNodes = entitiesNode.hasChildren() ? entitiesNode.nodes('a:Entity') : [],
                _this = this;
            this.entities = [];
            entityNodes.forEach( function ( item ) {
                var be = new BusinessEntity();
                be.deserialize(item, context.clone());
                _this.entities.push(be);
            });
        }
    };

    //endregion EntityCollection

    //region KeyValuePair

    function KeyValuePair ( key, value, valueAttributes ) {
        this.key = typeof key != 'undefined' ? key : null;
        this.value = typeof value != 'undefined' ? value : null;
        this.valueAttributes = typeof valueAttributes != 'undefined' ? valueAttributes : null;
        this.namespace = 'http://schemas.datacontract.org/2004/07/System.Collections.Generic';
    }

    KeyValuePair.prototype = {
        serialize: function ( serializationContext ) {
            var prefix = serializationContext.letter(this.namespace);
            var valueNode = {
                tag: prefix.concat(':value'),
                attr: this.valueAttributes
            };

            if ( this.value !== null ) {
                if (typeof this.value.serialize != 'undefined') {
                    var tmpVal = this.value.serialize(serializationContext);
                    if (typeof tmpVal == 'object') {
                        valueNode.nodes = Array.isArray(tmpVal) ? tmpVal : [ tmpVal ];
                    } else {
                        valueNode.val = tmpVal;
                    }
                } else {
                    valueNode.val = this.value;
                }
            } else {
                valueNode.attr = { 'i:nil':'true' };
            }

            return {
                tag: 'a:KeyValuePairOfstringanyType',
                nodes: [{
                    tag: prefix.concat(':key'),
                    val: this.key
                },
                    valueNode
                ]
            };
        },
        deserialize: function ( node, context ) {
            var ltr = context.letter(this.namespace),
                nodeName = {
                    value: ltr.concat(':value'),
                    key: ltr.concat(':key')
                },
                valueNode = node.node(nodeName.value),
                valueType = valueNode.get_nodeType(),
                cntxt;
            this.key = node.node(nodeName.key).text();
            context.extractFromNode(valueNode);
            if ( valueType !== null ) {
                switch ( valueType ) {
                    case COSA.Const.AttributeType.Bool:
                        this.value = valueNode.text() == 'true';
                        break;
                    case 'OrganizationResponseCollection':
                        this.value = new OrganizationResponseCollection();
                        this.value.deserialize(valueNode, context.clone());
                        break;
                    case 'AccessRights':
                    case 'string':
                        this.value = valueNode.text();
                        break;
                    case 'guid':
                        this.value = new XrmValue(valueNode.text(), 'guid');
                        break;
                    case 'Entity':
                        this.value = new BusinessEntity();
                        this.value.deserialize(valueNode, context.clone());
                        break;
                    case 'OptionSetValue':
                        this.value = new OptionSetValue();
                        this.value.deserialize(valueNode);
                        break;
                    case 'EntityReference':
                        this.value = new EntityReference();
                        this.value.deserialize(valueNode, context.clone());
                        break;
                    case 'EntityCollection':
                        this.value = new EntityCollection();
                        this.value.deserialize(valueNode, context.clone());
                        break;
                    case 'dateTime':
                        this.value = new XrmValue(new Date(valueNode.text()), 'dateTime');
                        break;
                    case 'int':
                        this.value = parseInt(valueNode.text());
                        break;
                    case 'Money':
                        this.value = new Money();
                        this.value.deserialize(valueNode);
                        break;
                    case 'decimal':
                    case 'double':
                        this.value = new XrmValue(parseFloat(valueNode.text()), valueType);
                        break;
                    case 'EntityMetadata':
                        cntxt = context.clone();
                        cntxt.extractFromNode(valueNode);
                        this.value = new EntityMetadata();
                        this.value.deserialize(valueNode, cntxt);
                        break;
                    case 'DateTimeAttributeMetadata':
                    case 'PicklistAttributeMetadata':
                    case 'StringAttributeMetadata':
                    case 'LookupAttributeMetadata':
                    case 'DecimalAttributeMetadata':
                    case 'BooleanAttributeMetadata':
                    case 'MoneyAttributeMetadata':
                    case 'DoubleAttributeMetadata':
                    case 'IntegerAttributeMetadata':
                    case 'StatusAttributeMetadata':
                    case 'StateAttributeMetadata':
                        cntxt = context.clone();
                        cntxt.extractFromNode(valueNode);
                        this.value = new AttributeMetadata();
                        this.value.deserialize(valueNode, cntxt);
                        break;
                    case 'ArrayOfRolePrivilege':
                        this.value = new RolePrivilegeCollection();
                        this.value.deserialize(valueNode, context.clone());
                        break;
                    case 'AliasedValue':
                        this.value = new AliasedValue();
                        this.value.deserialize(valueNode, context.clone());
                        break;
                }
            } else {
                this.value = valueNode.text();
            }
        }
    };

    //endregion KeyValuePair

    function AliasedValue () {
        this.attributeLogicalName = null;
        this.entityLogicalName = null;
        this.value = null;
    }

    AliasedValue.prototype = {
        deserialize: function ( node, serializationContext ) {
            if ( typeof node != 'undefined' && node !== null ) {
                this.attributeLogicalName = node.node('a:AttributeLogicalName').text();
                this.entityLogicalName = node.node('a:EntityLogicalName').text();
                this.value = node.node('a:Value').text();
            }
        }
    };


    //region RolePrivilegeCollection

    function RolePrivilegeCollection () {
        this.rolePrivileges = null;
        this.namespace = 'http://schemas.microsoft.com/crm/2011/Contracts';
    }

    RolePrivilegeCollection.prototype = {
        deserialize: function ( node, context ) {
            var ltr = context.letter(this.namespace),
                leafs = node.nodes(ltr.concat(':RolePrivilege'));
            this.rolePrivileges = leafs.map( function ( item ) {
                var rp = new RolePrivilege();
                rp.deserialize(item, context.clone());
                return rp;
            });
        }
    };

    //endregion RolePrivilegeCollection

    //region RolePrivilege

    function RolePrivilege() {
        this.businessUnitId = null;
        this.depth = null;
        this.privilegeId = null;
        this.namespace = 'http://schemas.microsoft.com/crm/2011/Contracts';
    }

    RolePrivilege.prototype = {
        deserialize: function ( node, context ) {
            var ltr = context.letter(this.namespace),
                names = {
                    businessUnitId: ltr.concat(':BusinessUnitId'),
                    depth: ltr.concat(':Depth'),
                    privilegeId: ltr.concat(':PrivilegeId')
                };
            this.businessUnitId = node.node(names.businessUnitId).text();
            this.depth = node.node(names.depth).text();
            this.privilegeId = node.node(names.privilegeId).text();
        }
    };

    //endregion RolePrivilege

    //region EntityMetadata

    function EntityMetadata () {
    }

    EntityMetadata.prototype = {
        deserialize: function ( node, context ) {
            var _this = this,
                childNodes = node.nodes();

            childNodes.forEach( function ( item ) {
                var nodeName = _removePrefix(item.name());
                var propName = _toCamelCase(nodeName);
                switch (nodeName) {
                    case 'CanBeInManyToMany':
                    case 'CanBePrimaryEntityInRelationship':
                    case 'CanBeRelatedEntityInRelationship':
                    case 'CanCreateAttributes':
                    case 'CanCreateCharts':
                    case 'CanCreateForms':
                    case 'CanCreateViews':
                    case 'CanModifyAdditionalSettings':
                    case 'IsAuditEnabled':
                    case 'IsConnectionsEnabled':
                    case 'IsCustomizable':
                    case 'IsDuplicateDetectionEnabled':
                    case 'IsMailMergeEnabled':
                    case 'IsMappable':
                    case 'IsReadOnlyInMobileClient':
                    case 'IsRenameable':
                    case 'IsValidForQueue':
                    case 'IsVisibleInMobile':
                    case 'IsVisibleInMobileClient':
                    case 'CanChangeHierarchicalRelationship':
                        _this[propName] = new ManagedProperty();
                        _this[propName].deserialize(item, context.clone());
                        break;
                    case 'EntityHelpUrlEnabled':
                    case 'IsValidForAdvancedFind':
                    case 'IsStateModelAware':
                    case 'IsReadingPaneEnabled':
                    case 'IsQuickCreateEnabled':
                    case 'IsManaged':
                    case 'IsIntersect':
                    case 'IsImportable':
                    case 'IsEnabledForTrace':
                    case 'IsEnabledForCharts':
                    case 'IsDocumentManagementEnabled':
                    case 'IsCustomEntity':
                    case 'IsChildEntity':
                    case 'IsBusinessProcessEnabled':
                    case 'IsAvailableOffline':
                    case 'IsActivityParty':
                    case 'IsActivity':
                    case 'IsAIRUpdated':
                    case 'EnforceStateTransitions':
                    case 'CanTriggerWorkflow':
                    case 'AutoRouteToOwnerQueue':
                        _this[propName] = item.text() == 'true';
                        break;
                    case 'Attributes':
                        var acoll = new AttributeMetadataCollection();
                        acoll.deserialize(item, context.clone());
                        _this[propName] = acoll.attributesMetadata;
                        break;
                    case 'ActivityTypeMask':
                    case 'ObjectTypeCode':
                        _this[propName] = parseInt(item.text());
                        break;
                    case 'Description':
                    case 'DisplayCollectionName':
                    case 'DisplayName':
                        _this[propName] = new LocalizedLabelCollection();
                        _this[propName].deserialize(item, context.clone());
                        break;
                    default:
                        var nilAttr = item.attr('i:nil');
                        _this[propName] = typeof nilAttr !== null && nilAttr == 'true' ? null : item.text();
                        break;
                }
            });
        }
    };

    //endregion EntityMetadata

    //region AttributeMetadataCollection

    function AttributeMetadataCollection() {
        this.attributesMetadata = [];
        this.namespace = 'http://schemas.microsoft.com/xrm/2011/Metadata';
    }

    AttributeMetadataCollection.prototype = {
        deserialize: function ( node, context ) {
            var ltr = context.letter(this.namespace),
                leafs = node.nodes(ltr.concat(':AttributeMetadata'));
            this.attributesMetadata = leafs.map(function ( item ) {
                var attrMeta = new AttributeMetadata();
                attrMeta.deserialize(item, context.clone());
                return attrMeta;
            });
        }
    };

    //endregion AttributeMetadataCollection

    //region AttributeMetadata

    function AttributeMetadata () {
        this.metadataId = null;
        this.hasChanged = null;
        this.attributeOf = null;
        this.attributeType = null;
    }

    AttributeMetadata.prototype = {
        deserialize: function ( node, context ) {
            var _this = this,
                childNodes = node.nodes(),
                cntxt;
            childNodes.forEach( function ( item ) {
                var nodeName = _removePrefix(item.name()),
                    propName = _toCamelCase(nodeName), nilAttr;
                switch (nodeName) {
                    case 'RequiredLevel':
                    case 'IsValidForAdvancedFind':
                    case 'IsRenameable':
                    case 'IsCustomizable':
                    case 'IsAuditEnabled':
                    case 'CanModifyAdditionalSettings':
                        _this[propName] = new ManagedProperty();
                        _this[propName].deserialize(item, context.clone());
                        break;
                    case 'Description':
                    case 'DisplayName':
                        _this[propName] = new LocalizedLabelCollection();
                        _this[propName].deserialize(item, context.clone());
                        break;
                    case 'Targets':
                        cntxt = context.clone();
                        cntxt.extractFromNode(item);
                        var values = new ValueNodeCollection();
                        values.deserialize(item, cntxt);
                        _this[propName] = values.values;
                        break;
                    case 'MaxLength':
                    case 'ColumnNumber':
                    case 'SourceType':
                    case 'SourceTypeMask':
                    case 'DefaultFormValue':
                    case 'MinValue':
                    case 'MaxValue':
                    case 'Precision':
                    case 'PrecisionSource':
                        nilAttr = item.attr('i:nil');
                        _this[propName] = typeof nilAttr != 'undefined' && nilAttr == 'true' ? null : parseInt(item.text());
                        break;
                    case 'CanBeSecuredForCreate':
                    case 'CanBeSecuredForRead':
                    case 'CanBeSecuredForUpdate':
                    case 'IsCustomAttribute':
                    case 'IsManaged':
                    case 'IsPrimaryId':
                    case 'IsPrimaryName':
                    case 'IsSecured':
                    case 'IsValidForCreate':
                    case 'IsValidForRead':
                    case 'IsValidForUpdate':
                    case 'IsLocalizable':
                    case 'IsLogical':
                        _this[propName] = item.text() == 'true';
                        break;
                    case 'AttributeTypeName':
                    case 'FormatName':
                        cntxt = context.clone();
                        cntxt.extractFromNode(item);
                        _this[propName] = new ValueNode();
                        _this[propName].deserialize(item, cntxt);
                        break;
                    case 'OptionSet':
                        _this[propName] = new OptionSetMetadata();
                        _this[propName].deserialize(item, context.clone());
                        break;
                    default:
                        nilAttr = item.attr('i:nil');
                        _this[propName] = typeof nilAttr != 'undefined' && nilAttr == 'true' ? null : item.text();
                        break;
                }
            });
        }
    };

    //endregion AttributeMetadata

    //region ValueNodeCollection

    function ValueNodeCollection () {
        this.values = [];
        this.namespace = 'http://schemas.microsoft.com/2003/10/Serialization/Arrays';
    }

    ValueNodeCollection.prototype = {
        deserialize: function ( node, context ) {
            var ltr = context.letter(this.namespace),
                childNodes = node.nodes(ltr.concat(':string'));

            this.values = childNodes.map( function ( item ) {
                return item.text();
            });
        }
    };

    //endregion ValueNodeCollection

    //region ValueNode

    function ValueNode () {
        this.value = '';
        this.namespace = 'http://schemas.microsoft.com/xrm/2013/Metadata';
    }

    ValueNode.prototype = {
        deserialize: function ( node, context ) {
            var ltr = context.letter(this.namespace),
                valueNode = node.node(ltr.concat(':Value'));
            this.value = typeof valueNode != 'undefined' ? valueNode.text() : null;
        }
    };

    //endregion ValueNode

    //region OptionSetMetadata

    function OptionSetMetadata () {
        this.namespace = 'http://schemas.microsoft.com/xrm/2011/Metadata';
    }

    OptionSetMetadata.prototype = {
        deserialize: function ( node, context ) {
            var childNodes = node.nodes(),
                ltr = context.letter(this.namespace),
                _this = this;
            childNodes.forEach( function ( item ) {
                var nodeName = _removePrefix( item.name()),
                    propName = _toCamelCase( nodeName );
                switch ( nodeName ) {
                    case 'Description':
                    case 'DisplayName':
                        _this[propName] = new LocalizedLabelCollection();
                        _this[propName].deserialize( item, context.clone() );
                        break;
                    case 'Options':
                        _this[propName] = item.nodes(ltr.concat(':OptionMetadata')).map( function ( item ) {
                            var option = new OptionMetadata();
                            option.deserialize( item, context.clone() );
                            return option;
                        });
                        break;
                    case 'TrueOption':
                    case 'FalseOption':
                        _this[propName] = new OptionMetadata();
                        _this[propName].deserialize( item, context.clone() );
                        break;
                    case 'IsManaged':
                    case 'IsGlobal':
                    case 'IsCustomOptionSet':
                        _this[propName] = item.text() == 'true';
                        break;
                    case 'IsCustomizable':
                        _this[propName] = new ManagedProperty();
                        _this[propName].deserialize( item, context.clone() );
                        break;
                    default:
                        var nilAttr = item.attr('i:nil');
                        _this[propName] = typeof nilAttr != 'undefined' && nilAttr == 'true' ? null : item.text();
                        break;
                }
            });
        }
    };

    //endregion OptionSetMetadata

    //region LocalizedLabelCollection

    function LocalizedLabelCollection () {
        this.localizedLabels = [];
        this.userLocalizedLabel = null;
    }

    LocalizedLabelCollection.prototype = {
        deserialize: function ( node, context ) {;
            var locLabelNodes = node.nodes('a:LocalizedLabels > a:LocalizedLabel');
            this.localizedLabels = typeof locLabelNodes == 'undefined' ?
                null :
                locLabelNodes.map(function ( item ) {
                    var locLabel = new LocalizedLabel();
                    locLabel.deserialize(item, context.clone());
                    return locLabel;
                });
            var ullNode = node.node('a:UserLocalizedLabel'),
                ullNilAttr = ullNode.attr('i:nil');
            if ( typeof ullNilAttr != 'undefined' && ullNilAttr == 'true' ) {
                this.userLocalizedLabel = null;
            } else {
                this.userLocalizedLabel = new LocalizedLabel();
                this.userLocalizedLabel.deserialize( ullNode, context.clone() );
            }
        }
    };

    //endregion LocalizedLabelCollection

    //region LocalizedLabel

    function LocalizedLabel () {
        this.metadataId = null;
        this.hasChanged = null;
        this.isManaged = null;
        this.label = null;
        this.languageCode = null;
        this.namespace = 'http://schemas.microsoft.com/xrm/2011/Metadata';
    }

    LocalizedLabel.prototype = {
        deserialize: function ( node, context ) {
            var root = node,
                ltr = context.letter(this.namespace),
                names = {
                    metadataId: ltr.concat(':MetadataId'),
                    hasChanged: ltr.concat(':HasChanged')
                };
            this.metadataId = typeof root.node(names.metadataId).attr('i:nil') != 'undefined' ? null : root.node(names.metadataId).text();
            this.hasChanged = typeof root.node(names.hasChanged).attr('i:nil') != 'undefined' ? null : root.node(names.hasChanged).text();
            this.isManaged = root.node('a:IsManaged').text() == 'true';
            this.label = root.node('a:Label').text();
            this.languageCode = root.node('a:LanguageCode').text();
        }
    };

    //endregion LocalizedLabel

    //region ManagedProperty

    function ManagedProperty ( canBeChanged, managedPropertyLogicalName, value ) {
        this.canBeChanged = typeof canBeChanged != 'undefined' ? canBeChanged : null;
        this.managedPropertyLogicalName = typeof managedPropertyLogicalName != 'undefined' ? managedPropertyLogicalName : null;
        this.value = typeof value != 'undefined' ? value : null;
    }

    ManagedProperty.prototype = {
        deserialize: function ( node, context ) {
            this.canBeChanged = node.node( 'a:CanBeChanged').text() == 'true';
            this.managedPropertyLogicalName = node.node('a:ManagedPropertyLogicalName').text();
            this.value = node.node('a:Value').text();
        }
    };

    //endregion ManagedProperty

    //region OptionMetadata

    function OptionMetadata() {
        this.value = null;
    }

    OptionMetadata.prototype = {
        deserialize: function ( node, context ) {
            var childNodes = node.nodes(),
                _this = this;
            childNodes.forEach( function ( item ) {
                var nodeName = _removePrefix( item.name() );
                var propName = _toCamelCase( nodeName );
                switch ( nodeName ) {
                    case 'Description':
                    case 'Label':
                        _this[propName] = new LocalizedLabelCollection();
                        _this[propName].deserialize( item, context.clone() );
                        break;
                    case 'IsManaged':
                        _this[propName] = item.text() == 'true';
                        break;
                    case 'Value':
                    case 'DefaultStatus':
                    case 'State':
                        _this[propName] = parseInt(item.text());
                        break;
                    default:
                        var nilAttr = item.attr('i:nil');
                        _this[propName] = typeof nilAttr != 'undefined' && nilAttr == 'true' ? null : item.text();
                        break;
                }
            } );
        }
    };

    //endregion OprionMetadata

    //region KeyValuePairCollection

    function KeyValuePairCollection ( ) {
    }

    KeyValuePairCollection.prototype = {
        deserialize: function ( node, context, keyValuePairType ) {
            var selector = typeof keyValuePairType == 'undefined' || keyValuePairType == COSA.Const.KeyValuePairType.StringAnyType ? 'a:KeyValuePairOfstringanyType' : 'a:KeyValuePairOfstringstring',
                kvnodes = node.nodes(selector),
                _this = this;
            kvnodes.forEach( function ( item ) {
                var kvpair = new KeyValuePair();
                kvpair.deserialize(item, context.clone());
                _this[kvpair.key] = kvpair.value;
            });
        }
    };

    //endregion KeyValuePairCollection

    //region ColumnSet

    function ColumnSet ( columns ) {
        this.columns = typeof columns != 'undefined' && Array.isArray(columns) ? columns : null;
    }

    //TODO: Add encoding
    ColumnSet.prototype = {
        serialize: function ( serializationContext ) {
            var colAttrs = {};
            var prefix = serializationContext.letter();
            colAttrs['xmlns:'.concat(prefix)] = 'http://schemas.microsoft.com/2003/10/Serialization/Arrays';
            var colNode = {
                tag: 'a:Columns',
                attr: colAttrs
            };
            if ( this.columns !== null ) {
                colNode.nodes = this.columns.map( function ( item ) {
                    return {
                        tag: prefix.concat(':string'),
                        val: item
                    }
                });
            }
            return [{
                tag: 'a:AllColumns',
                val: ( this.columns === null || this.columns.length === 0 )
            }, colNode];
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:ColumnSet' });
        }
    };

    //endregion ColumnSet

    //region ActivityParty

    function ActivityParty ( partyId ) {
        this.partyId = typeof partyId != 'undefined' ? partyId : null;
    }

    ActivityParty.prototype = {
        serialize: function ( sc ) {
            var be = new BusinessEntity('activityparty', null, {
                partyid: this.partyId
            });
            return be.serialize(sc);
        }
    };

    //endregion ActivityParty

    //region BE

    function BusinessEntity( logicalName, id, attributes ) {
        this.logicalName = typeof logicalName != 'undefined' ? logicalName : null;
        this.id = typeof id != 'undefined' && id !== null ? id : '{00000000-0000-0000-0000-000000000000}';
        this.attributes = typeof attributes != 'undefined' ? attributes : {};
        this.type = 'BusinessEntity';
    }

    BusinessEntity.prototype = {
        serialize: function ( sc ) {
            var attrNodes = [];
            for ( var attr in this.attributes ) {
                var attrValue = this.attributes[attr];
                if ( attrValue === null || typeof attrValue.toKeyValuePair == 'undefined' ) {
                    attrValue = new XrmValue(attrValue);
                }
                attrNodes.push(attrValue.toKeyValuePair(attr, sc));
            }
            return [{
                tag: 'a:Attributes',
                nodes: attrNodes.map(function ( item ) { return item.serialize( sc ); })
            }, {
                tag: 'a:EntityState',
                attr: { 'i:nil':'true' }
            }, {
                tag: 'a:FormattedValues'
            }, {
                tag: 'a:Id',
                val: _encodeValue(this.id)
            }, {
                tag: 'a:LogicalName',
                val: _encodeValue(this.logicalName)
            }, {
                tag: 'a:RelatedEntities'
            }];
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:Entity' });
        },
        deserialize: function ( node, context ) {
            var attributesNode = node.node('a:Attributes'),
                formattedValuesNode = node.node('a:FormattedValues'),
                attrs, formattedValues;
            if ( typeof attributesNode != 'undefined' ) {
                attrs = new KeyValuePairCollection();
                attrs.deserialize(attributesNode, context.clone());
            }

            if ( typeof formattedValuesNode != 'undefined' ) {
                formattedValues = new KeyValuePairCollection();
                formattedValues.deserialize(formattedValuesNode, context.clone(), COSA.Const.KeyValuePairType.StringString);
                for (var fv in formattedValues ) {
                    if ( attrs.hasOwnProperty(fv) ) {
                        var valueType = typeof attrs[fv];
                        switch ( valueType ) {
                            case 'boolean':
                                attrs[fv] = new TwoOptions(attrs[fv]);
                                break;
                            case 'number':
                                attrs[fv] = new XrmValue(attrs[fv], 'int');
                                break;
                            case 'string':
                                attrs[fv] = new XrmValue(attrs[fv], 'string');
                                break;
                        }
                        attrs[fv].formattedValue = formattedValues[fv];
                    }
                }
            }
            this.attributes = typeof attrs != 'undefined' ? attrs : {};
            this.id = node.node('a:Id').text();
            this.logicalName = node.node('a:LogicalName').text();
        }
    };

    function TwoOptions ( value ) {
        this.value = typeof value != 'undefined' ? value : false;
        this.formattedValue = '';
    }

    //endregion BE

    //region OrderExpression

    function OrderExpression ( attributeName, orderType ) {
        this.attributeName = typeof attributeName != 'undefined' ? attributeName : null;
        this.orderType = typeof orderType != 'undefined' ? orderType : null;
    }

    OrderExpression.prototype = {
        serialize: function () {
            if ( this.attributeName === null || this.orderType === null ) {
                throw new Error(COSA.Const.Error.REQUIRED_ATTRIBUTES_MISSED);
            }
            return {
                tag: 'a:OrderExpression',
                nodes: [{
                    tag: 'a:AttributeName',
                    val: this.attributeName
                }, {
                    tag: 'a:OrderType',
                    val: this.orderType
                }]
            };
        }
    };

    //endregion OrderExpression

    //region PageInfo

    function PageInfo () {
        this.count = 0;
        this.pageNumber = 0;
        this.pagingCookie = null;
        this.returnTotalRecordCount = false;
    }

    PageInfo.prototype = {
        serialize: function () {
            return {
                tag: 'a:PageInfo',
                nodes: [{
                    tag: 'a:Count',
                    val: this.count
                }, {
                    tag: 'a:PageNumber',
                    val: this.pageNumber
                }, {
                    tag: 'a:PagingCookie',
                    attr: this.pagingCookie === null ? { 'i:nil':'true' } : {},
                    val: this.pagingCookie
                }, {
                    tag: 'a:ReturnTotalRecordCount',
                    val: this.returnTotalRecordCount
                }]
            };
        }
    };

    //endregion PageInfo

    //region QueryExpression

    function QueryExpression ( entityLogicalName ) {
        this.entityName = entityLogicalName;
        this.columnSet = null;
        this.criteria = null;
        this.distinct = false;
        this.linkEntities = [];
        this.orders = [];
        this.noLock = false;
        this.topCount = null;
        this.pageInfo = new PageInfo()
    }

    QueryExpression.prototype = {
        serialize: function ( serializationContext ) {
            var columnSetNodes = this.columnSet.serialize(serializationContext);
            var orderNodes = this.orders.map(function ( order ) { return order.serialize(); });
            var linkEntitiesNode = this.linkEntities.length > 0 ? {
                tag: 'a:LinkEntities',
                nodes: this.linkEntities.map(function ( item ) { return item.serialize(serializationContext); })
            } : {
                tag: 'a:LinkEntities'
            };

            if (this.criteria == null) {
                this.criteria = new FilterExpression();
            }

            var criteriaNode = this.criteria.serialize( true, serializationContext );
            var res = [{
                tag: 'a:ColumnSet',
                nodes: columnSetNodes
            }, criteriaNode,{
                tag: 'a:Distinct',
                val: this.distinct
            }, {
                tag: 'a:EntityName',
                val: this.entityName
            }, linkEntitiesNode, {
                tag: 'a:Orders',
                nodes: orderNodes
            },
                this.pageInfo.serialize(),
                {
                    tag: 'a:NoLock',
                    val: this.noLock
                }
            ];

            if (this.topCount !== null) {
                res.push({ tag: 'a:TopCount', val: this.topCount });
            }

            return res;
        },
        addOrder: function ( orderExpression ) {
            this.orders.push( orderExpression );
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair(key, this, { 'i:type':'a:QueryExpression' });
        }
    };

    //endregion QueryExpression

    //region LinkEntity

    function LinkEntity ( initials ) {
        this.linkFromEntityName = initials.hasOwnProperty('linkFromEntityName') ? initials.linkFromEntityName : '';
        this.linkToEntityName = initials.hasOwnProperty('linkToEntityName') ? initials.linkToEntityName : '';
        this.linkFromAttributeName = initials.hasOwnProperty('linkFromAttributeName') ? initials.linkFromAttributeName : '';
        this.linkToAttributeName = initials.hasOwnProperty('linkToAttributeName') ? initials.linkToAttributeName : '';
        this.joinOperator = initials.hasOwnProperty('joinOperator') ? initials.joinOperator : '';
        this.columns = initials.hasOwnProperty('columns') ? initials.columns : null;
        this.entityAlias = '';
    }

    LinkEntity.prototype = {
        serialize: function ( serializationContext ) {

            return {
                tag: 'a:LinkEntity',
                nodes: [{
                        tag: 'a:Columns',
                        nodes: this.columns.serialize(serializationContext)
                    }, {
                        tag: 'a:EntityAlias',
                        val: this.entityAlias
                    }, {
                        tag: 'a:JoinOperator',
                        val: this.joinOperator
                    }, {
                        tag: 'a:LinkCriteria'
                    }, {
                        tag: 'a:LinkFromAttributeName',
                        val: this.linkFromAttributeName
                    }, {
                        tag: 'a:LinkFromEntityName',
                        val: this.linkFromEntityName
                    }, {
                        tag: 'a:LinkToAttributeName',
                        val: this.linkToAttributeName
                    }, {
                        tag: 'a:LinkToEntityName',
                        val: this.linkToEntityName
                    }
                ]
            };
        }
    };

    //endregion LinkEntity

    //region FilterExpression

    function  FilterExpression( filterOperator ) {
        this.filterOperator = typeof filterOperator != 'undefined' ? filterOperator : COSA.Const.FilterOperator.AND;
        this.conditions = [];
        this.filters = [];
    }

    FilterExpression.prototype = {
        serialize: function ( isRoot, serializationContext ) {
            var conditionNode = {
                tag: 'a:Conditions'
            };
            var filtersNode = {
                tag: 'a:Filters'
            };

            if (this.conditions.length > 0) {
                conditionNode.nodes = this.conditions.map(function ( item ) { return item.serialize(serializationContext); });
            }

            if ( this.filters.length > 0 ) {
                filtersNode.nodes = this.filters.map( function ( item ) { return item.serialize(false, serializationContext); });
            }

            return {
                tag: isRoot ? 'a:Criteria' : 'a:FilterExpression',
                nodes: [conditionNode, {
                    tag: 'a:FilterOperator',
                    val: this.filterOperator
                }, filtersNode]
            }
        },
        addCondition: function ( conditionExpression ) {
            this.conditions.push( conditionExpression );
        },
        addFilter: function ( filterExpression ) {
            this.filters.push( filterExpression );
        }
    };

    //endregion FilterExpression

    //region ConditionExpression

    function ConditionExpression( attributeName, operator, values ) {
        this.attributeName = typeof attributeName != 'undefined' ? attributeName : null;
        this.operator = typeof operator != 'undefined' ? operator : null;
        this.values = typeof values != 'undefined' ? values : null;
    }

    ConditionExpression.prototype = {
        serialize: function ( serializationContext ) {
            var valueNodes = [],
                ltr = serializationContext.letter(),
                valuesNodeAttr = {};
            valuesNodeAttr['xmlns:'.concat(ltr)] = 'http://schemas.microsoft.com/2003/10/Serialization/Arrays';
            serializationContext.reserveLetter(ltr, 'http://schemas.microsoft.com/2003/10/Serialization/Arrays');

            var ltr2 = serializationContext.letter(),
                namespaceDefinition = 'xmlns:'.concat(ltr2);

            if (!Array.isArray(this.values)) {
                var valueNode = {
                    tag: ltr.concat(':anyType'),
                    attr: { 'i:type': ltr2.concat(':string') },
                    val: this.values
                };
                valueNode.attr[namespaceDefinition] = 'http://www.w3.org/2001/XMLSchema';
            }

            return {
                tag: 'a:ConditionExpression',
                nodes: [{
                    tag: 'a:AttributeName',
                    val: this.attributeName
                }, {
                    tag: 'a:Operator',
                    val: this.operator
                }, {
                    tag: 'a:Values',
                    attr: valuesNodeAttr,
                    nodes: [valueNode]
                }, {
                    tag: 'a:EntityName',
                    attr: {'i:nil': 'true'}
                }]
            };
        }
    };

    //endregion ConditionExpression

    //region ExecuteMultipleSettings

    function ExecuteMultipleSettings ( continueOnError, returnResponses ) {
        this.continueOnError = typeof continueOnError != 'undefined' ? continueOnError : null;
        this.returnResponses = typeof returnResponses != 'undefined' ? returnResponses : null;
    }

    ExecuteMultipleSettings.prototype = {
        serialize: function () {
            return [{
                tag: 'c:ContinueOnError',
                val: this.continueOnError
            }, {
                tag: 'c:ReturnResponses',
                val: this.returnResponses
            }];
        },
        toKeyValuePair: function ( key ) {
            return new KeyValuePair( key, this, { 'i:type':'c:ExecuteMultipleSettings', 'xmlns:c':'http://schemas.microsoft.com/xrm/2012/Contracts' });
        }
    };

    //endregion ExecuteMultipleSettings

    //region OrganizationRequestCollection

    function OrganizationRequestCollection ( requests ) {
        this.requests = typeof requests != 'undefined' && Array.isArray(requests) ? requests : null;
    }

    OrganizationRequestCollection.prototype = {
        serialize: function ( sc ) {
            if ( this.requests === null ) {
                throw new Error(COSA.Const.Error.REQUIRED_ATTRIBUTES_MISSED);
            }

            return this.requests.map(function ( request ) {
                return request.serialize( sc );
            });
        },
        toKeyValuePair: function ( key, sc ) {
            sc.reserveLetter('c', 'http://schemas.microsoft.com/xrm/2012/Contracts');
            return new KeyValuePair( key, this, { 'i:type':'c:OrganizationRequestCollection', 'xmlns:c':'http://schemas.microsoft.com/xrm/2012/Contracts' });
        }
    };

    //endregion OrganizationRequestCollection

    //region OperationContext

    function OperationContext ( parameters ) {
        this.reserved = typeof parameters != 'undefined' && parameters.hasOwnProperty('initiallyReserved') ? parameters.initiallyReserved : {};
        this.isSingle = typeof parameters != 'undefined' && parameters.hasOwnProperty('isSingle') ? parameters.isSingle : true;
        this.responseProvider = typeof parameters != 'undefined' && parameters.hasOwnProperty('responseProvider') ? parameters.responseProvider : null;
    }

    OperationContext.prototype = {
        letter: function ( namespace ) {
            var letters = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'].reverse();
            if ( typeof namespace != 'undefined' && namespace !== null ) {
                for ( var nsl in this.reserved ) {
                    if ( this.reserved.hasOwnProperty(nsl) && this.reserved[nsl] == namespace ) {
                        return nsl;
                    }
                }
            } else {
                var testLetter;
                while ( letters.length > 0 ) {
                    testLetter = letters.pop();
                    if (!this.reserved.hasOwnProperty(testLetter)) {
                        return testLetter;
                    }
                }
            }
        },
        registerNamespace: function ( nsLetter, namespace ) {
            if ( !this.reserved.hasOwnProperty(nsLetter) ) {
                this.reserved[nsLetter] = namespace;
            } else {
                throw new Error('already reserved');
            }
        },
        extractFromNode: function ( node ) {
            var attrs = node.attr();
            var res = {};
            for ( var attr in attrs ) {
                if ( attrs.hasOwnProperty(attr) && attr.startsWith('xmlns') ) {
                    this.reserved[attr.split(':')[1]] = attrs[attr]
                }
            }
            return res;
        },
        clone: function () {
            var params = {
                isSingle: this.isSingle,
                responseProvider: this.responseProvider,
                initiallyReserved: this.reserved
            };
            return new OperationContext(params);
        }
    };

    //endregion OperationContext

    //region SerializationContext

    function SerializationContext ( initiallyReservedLetters, isSingle ) {
        this.reserved = typeof initiallyReservedLetters != 'undefined' ? initiallyReservedLetters : {};
        this.isSingle = typeof isSingle != 'undefined' ? isSingle : true;
    }

    SerializationContext.prototype = {
        letter: function ( namespace ) {
            var letter;
            if ( typeof namespace != 'undefined' ) {
                for ( var nsletter in this.reserved ) {
                    if (this.reserved[nsletter] == namespace) {
                        letter = nsletter;
                        break;
                    }
                }
            } else {
                var availableLetters = ['c', 'd', 'e'].reverse(),
                    testLetter;
                while (availableLetters.length > 0 && typeof letter === 'undefined') {
                    testLetter = availableLetters.pop();
                    if ( !this.reserved.hasOwnProperty(testLetter) ){
                        letter = testLetter;
                    }
                }
            }

            return letter;
        },
        reserveLetter: function ( letter, namespace ) {
            this.reserved[letter] = namespace;
        }
    };

    //endregion SerializationContext

    //region OrganizationResponseCollection

    function OrganizationResponseCollection ( ) {
        this.responseItems = [];
        this.namespace = 'http://schemas.microsoft.com/xrm/2012/Contracts';
    }

    OrganizationResponseCollection.prototype = {
        deserialize: function ( node, context ) {
            var letter = context.letter(this.namespace),
                responseItemNodes = node.nodes(letter.concat(':ExecuteMultipleResponseItem')),
                _this = this;

            responseItemNodes.forEach( function ( item ) {
                var emri = new ExecuteMultipleResponseItem();
                emri.deserialize(item, context);
                _this.responseItems.push(emri);
            });
        }
    };

    //endregion OrganizationResponseCollection

    //region ExecuteMultipleResponseItem

    function ExecuteMultipleResponseItem () {
        this.fault = null;
        this.requestIndex = 0;
        this.response = null;
        this.namespace = 'http://schemas.microsoft.com/xrm/2012/Contracts';
    }

    ExecuteMultipleResponseItem.prototype = {
        deserialize: function ( node, context ) {
            var letter = context.letter(this.namespace),
                faultNode = node.node(letter.concat(':Fault')),
                responseNode = node.node(letter.concat(':Response'));
            this.requestIndex = parseInt(node.node(letter.concat(':RequestIndex')).text());
            if ( faultNode.hasChildren() ) {
                this.fault = new OrganizationServiceFault();
                this.fault.deserialize(faultNode);
            } else if ( responseNode.attr('i:nil') === null ) {
                context.extractFromNode(responseNode);
                var respClass = context.responseProvider.provideResponse(responseNode.get_nodeType());
                this.response = new respClass();
                this.response.deserialize(responseNode, context.clone());
            }
        }
    };

    //endregion ExecuteMultipleResponseItem

    //region OrganizationServiceFault

    function OrganizationServiceFault () {
        this.errorCode = 0;
        this.errorDetails = '';
        this.message = '';
        this.timestamp = null;
        this.innerFault = null;
        this.traceText = '';
    }

    OrganizationServiceFault.prototype = {
        deserialize: function ( node ) {
            var childNodes = typeof node != 'undefined' && node !== null ? node.nodes() : null;
            if ( typeof childNodes != 'undefined' && childNodes !== null ) {
                var _this = this;
                childNodes.forEach( function ( childNode ) {
                    var nodeName = childNode.name(),
                        unprefixedNodeName = nodeName.split(':')[1];

                    switch ( unprefixedNodeName ) {
                        case 'ErrorCode':
                            _this.errorCode = parseInt(childNode.text());
                            break;
                        case 'Message':
                            _this.message = childNode.text();
                            break;
                        case 'Timestamp':
                            _this.timestamp = new Date(childNode.text());
                            break;
                        case 'InnerFault':
                            if ( childNode.attr('i:nil') === null ) {
                                _this.innerFault = new OrganizationServiceFault();
                                _this.innerFault.deserialize(childNode);
                            }
                            break;
                        default:
                            if ( childNode.attr('i:nil') === null ) {
                                _this[_toCamelCase(nodeName)] = childNode.text();
                            }
                            break;
                    }
                });
            }
        }
    };

    //endregion OrganizationServiceFault

    //region Fault

    function Fault ( ) {
        this.faultcode = '';
        this.faultstring = '';
        this.detail = null;
    }

    Fault.prototype = {
        deserialize: function ( node ) {
            var nodes = typeof node != 'undefined' && node !== null ? node.nodes() : null;
            if ( typeof nodes != 'undefined' && nodes !== null ) {
                var _this = this;
                nodes.forEach( function ( childNode ) {
                    var nodeName = childNode.name();
                    switch ( nodeName ) {
                        case 'detail':
                            var orgServiceFaultNode = childNode.node('OrganizationServiceFault');
                            _this.detail = new OrganizationServiceFault();
                            _this.detail.deserialize(orgServiceFaultNode);
                            break;
                        default:
                            _this[nodeName] = childNode.text();
                            break;
                    }
                });
            }
        }
    };

    //endregion Fault

    //region AccessRights

    function AccessRights ( accessRightsStr ) {
        var res = {
            hasReadAccess: false,
            hasWriteAccess: false,
            hasAppendAccess: false,
            hasAppendToAccess: false,
            hasCreateAccess: false,
            hasDeleteAccess: false,
            hasShareAccess: false,
            hasAssignAccess: false
        };

        var tmp = accessRightsStr.split(' ');
        tmp.forEach(function ( item ) {
            var prpName = 'has'.concat(item);
            if (res.hasOwnProperty(prpName)) {
                res[prpName] = true;
            }
        });

        return res;
    }

    //endregion AccessRights

    return {
        XrmValue: XrmValue,
        BusinessEntity: BusinessEntity,
        EntityReference: EntityReference,
        EntityCollection: EntityCollection,
        KeyValuePair: KeyValuePair,
        ColumnSet: ColumnSet,
        OrderExpression: OrderExpression,
        PageInfo: PageInfo,
        QueryExpression: QueryExpression,
        LinkEntity: LinkEntity,
        FilterExpression: FilterExpression,
        ConditionExpression: ConditionExpression,
        ExecuteMultipleSettings: ExecuteMultipleSettings,
        OrganizationRequestCollection: OrganizationRequestCollection,
        SerializationContext: SerializationContext,
        Fault: Fault,
        OperationContext: OperationContext,
        KeyValuePairCollection: KeyValuePairCollection,
        AccessRights: AccessRights,
        OptionSetValue: OptionSetValue,
        Money: Money,
        ActivityParty: ActivityParty
    };
})();